
#include <RevEng/Mutex.hpp>
#include "CondVarWin32.hpp"

namespace RevEng
{
	CondVarWin32::CondVarWin32() : m_nWaitersCount(0)
	{
		InitializeCriticalSection(&m_pWaitersCountGuard);

		m_ahEvents[SIGNAL] = CreateEvent(NULL, FALSE, FALSE, NULL);
		m_ahEvents[BROADCAST] = CreateEvent(NULL, TRUE, FALSE, NULL);
	}


	CondVarWin32::~CondVarWin32()
	{
		CloseHandle(m_ahEvents[SIGNAL]);
		CloseHandle(m_ahEvents[BROADCAST]);
		DeleteCriticalSection(&m_pWaitersCountGuard);
	}


	void CondVarWin32::Wait( IMutex* Mut )
	{
		DWORD Result;
		bool LastWaiter;

		//Aumentar la cantidad de pacientes
		EnterCriticalSection(&m_pWaitersCountGuard);
		++m_nWaitersCount;
		LeaveCriticalSection(&m_pWaitersCountGuard);

		//Liberar aqui el mutex para permitir a otros hilos manipular el recurso
		//usado para notificar el resto
		Mut->Unlock();

		//Suspender el hilo actual hasta se invoque a Signal o Broadcast.
		Result = WaitForMultipleObjects(2, m_ahEvents, FALSE, INFINITE);

		//Ahora que se a derpertado este hilo, verificar si es el ultimo.
		EnterCriticalSection(&m_pWaitersCountGuard);
		--m_nWaitersCount;
		if ((Result - WAIT_OBJECT_0 == BROADCAST) && (m_nWaitersCount == 0))
			LastWaiter = true;
		else
			LastWaiter = false;		
		LeaveCriticalSection(&m_pWaitersCountGuard);

		//Como este es el ultimo hilo, entonces reseteamos el evento BROADCAST
		if (LastWaiter)
			ResetEvent(m_ahEvents[BROADCAST]);
		
		//Bloqueamos nuevamente
		Mut->Lock();
	}


	void CondVarWin32::Signal()
	{
		bool bHaveWaiters;

		EnterCriticalSection(&m_pWaitersCountGuard);
		bHaveWaiters  = m_nWaitersCount > 0;
		LeaveCriticalSection(&m_pWaitersCountGuard);

		if (bHaveWaiters)
			SetEvent(m_ahEvents[SIGNAL]);
	}


	void CondVarWin32::Broadcast()
	{
		bool bHaveWaiters;

		EnterCriticalSection(&m_pWaitersCountGuard);
		bHaveWaiters  = m_nWaitersCount > 0;
		LeaveCriticalSection(&m_pWaitersCountGuard);

		if (bHaveWaiters)
			SetEvent(m_ahEvents[BROADCAST]);
	}
}
